//***************************************************************************
//   Copyright(c)2020, Xidian University D405.
//   All rights reserved
//
//   IP Name         :   Null
//   File name       :   np_dma_decode.v
//   Module name     :   np_dma_decode
//   Author          :   Wang Zekun
//   Date            :   2022/07/10
//   Version         :   v3.0
//   Verison History :   v1.0/v1.1/v2.0/v2.1/2.2/v2.3/v2.4/2.5/2.6/2.7/v2.8/v2.9/v2.91/2.92/v3.0
//   Edited by       :   Wang Zekun
//   Modification history : v1.0 Initial revision
//                          v1.1 add parameter AXI_ADDR_WIDTH
//                          v2.0 add ring length output to suit version2
//                          v2.1 add new register according to 0418 discussion:
//                               rx/tx desc address --40bit
//                          v2.2 add rx pending out count,delete axi_s_port_select_o
//                               add rx_fifo_empty_i for count enable
//                               add pending count enable in Rx control register
//                          v2.3 sync signals from ahbclk to axiclk in np_dma_decode, do not sync in other module
//                          v2.4 add ioc int signal and chasing desc tail address int
//                          v2.5 determine 12bit interrupt
//                          v2.6 line 308: add declaration of wire pending_cnt_en. zyc
//                          v2.7 change parameter NP_DMA_BASE_ADDR to dma_base_addr_i port
//                          v2.8 rx/tx_process_flag to RO
//                          v2.9 only detect timeout when rx_process_flag is high
//                          v2.91 when desc_tail_int happen, DMA need to refresh tail_desc_register,so add new signals rx/tx_tail_halted
//                                desc_tail_int is triggered by level instead of pulse.
//                          v2.92 fix rx_mac_int.
//                          v3.0 sync
// ----------------------------------------------------------------------------
// Version v3.0      Date(2022/07/10)
// Abstract : Hardforward DMA unit configure interface
//
//-----------------------------------------------------------------------------
// Programmer's model
// Base Address 0x8FFF_0000
// 0x00 RW    [31:0]
// 0x04 RW    [31:0]
// 0x08 RW    [31:0]
// 0x0C RW    [31:0]
// 0x10 RO    [31:0]
//-----------------------------------------------------------------------------
//interface list :
//                AHB

module np_dma_decode #(
  parameter  AXI_LIB_WIDTH            = 13,
  parameter  AXI_ADDR_WIDTH           = 40,
  //parameter  NP_DMA_BASE_ADDR        	=	32'h8FFF_0000,
  parameter  CH0_Rx_Control_addr		  =	32'h0000_0000,
  parameter  CH0_RxDesc_Haddr         =	32'h0000_0004,
	parameter  CH0_RxDesc_Laddr	        =	32'h0000_0008,
  parameter  CH0_RxDesc_Tail_Haddr	  =	32'h0000_000C,
  parameter  CH0_RxDesc_Tail_Laddr		=	32'h0000_0010,
  parameter  CH0_RxDesc_Ring_Length   =	32'h0000_0014,
  parameter  CH0_Tx_Control_addr	    =	32'h0000_0018,
  parameter  CH0_TxDesc_Haddr         =	32'h0000_001C,
	parameter  CH0_TxDesc_Laddr	        =	32'h0000_0020,
  parameter  CH0_TxDesc_Tail_Haddr	  =	32'h0000_0024,
  parameter  CH0_TxDesc_Tail_Laddr		=	32'h0000_0028,
  parameter  CH0_TxDesc_Ring_Length   =	32'h0000_002C,
  parameter  CH0_Interrupt_Enable		  =	32'h0000_0030,
  parameter  CH0_Interrupt_Status	    =	32'h0000_0034, //read only
	parameter  CH0_Interrupt_Clear   	  =	32'h0000_0038,
  parameter  CH0_Pendingout_cnt 	    =	32'h0000_003c,
	parameter  CH0_AXI_Status   	      =	32'h0000_0040  //read only
)

(
  input  wire [31:0]                    dma_base_addr_i, //NP_DMA_BASE_ADDR
  // AHB Inputs
  input  wire                           hclk_i,      // AHB clock
  input  wire                           hresetn_i,   // system bus reset
  input  wire                           hsel_i,      // AHB peripheral select
  input  wire                           hready_i,    // AHB ready input
  input  wire  [1:0]                    htrans_i,    // AHB transfer type
  input  wire  [2:0]                    hsize_i,     // AHB hsize
  input  wire                           hwrite_i,    // AHB hwrite
  input  wire [31:0]                    haddr_i,     // AHB address bus
  input  wire [31:0]                    hwdata_i,    // AHB write data bus
  // AHB Outputs          
  output wire                           hreadyout_o, // AHB ready output to S->M mux
  output wire                           hresp_o,     // AHB response
  output wire [31:0]                    hrdata_o,

  // m_axi_aclk domain signals
  input  wire                           m_axi_aclk_i,
  input  wire                           m_axi_aresetn_i,

  input  wire [2:0]                     rx_process_flag_i,
  input  wire [2:0]                     tx_process_flag_i,

  input  wire [AXI_ADDR_WIDTH-1:0]      rdesc_addr_current_i,
  input  wire [AXI_ADDR_WIDTH-1:0]      tdesc_addr_current_i,


  output wire [31:0]                    rxdesc_ring_length_o, //axi
  output wire [31:0]                    txdesc_ring_length_o, //axi

  output wire [AXI_ADDR_WIDTH-1:0]      rx_desc_addr_o, //axi
  output wire [AXI_ADDR_WIDTH-1:0]      tx_desc_addr_o, //axi

  input  wire                           rx_ioc_int_i,   //need to sync at hclk
  input  wire                           tx_ioc_int_i,   //need to sync at hclk

  input  wire                           rx_own_int_i,   //need to sync at hclk
  input  wire                           tx_own_int_i,   //need to sync at hclk

  input  wire                           rx_mac_int_i,   //need to sync at hclk
  input  wire                           tx_mac_int_i,   //need to sync at hclk

  input  wire                           rx_complete_int_i, //need to sync at hclk
  input  wire                           tx_complete_int_i, //need to sync at hclk
  
  input  wire                           rx_fifo_empty_i,

  output wire                           rx_start_o,
  output wire                           tx_start_o,

  output wire                           clr_rx_complete_o,
  output wire                           clr_tx_complete_o,

  output wire                           rxfifo_timeout_valid_o,

  output wire                           np_dma_irq_o,             // NP DMA transmassion irq

  output wire                           rx_tail_halted_o,
  output wire                           tx_tail_halted_o,
  
  output wire                           rx_buf_unavlib_int_clr_o,
  output wire                           tx_buf_unavlib_int_clr_o

);


  wire          read_valid;
  wire          write_valid;
  wire   [11:0] nxt_word_addr;
  reg           read_enable;
  reg           write_enable;
  reg    [11:0] word_addr;
  reg    [31:0] read_word;
  reg    [31:0] read_mux_word;

  wire          write_enable00; // Write enable for ???? register
  wire          write_enable04; // Write enable for ???? register
  wire          write_enable08;
  wire          write_enable0c;
  wire          write_enable10;
  wire          write_enable14;
  wire          write_enable18;
  wire          write_enable1c;
  wire          write_enable20;
  wire          write_enable24;
  wire          write_enable28;
  wire          write_enable2c;
  wire          write_enable30;
  wire          write_enable34;
  wire          write_enable38;
  wire          write_enable3c;
  wire          write_enable40;

  //register files
  reg    [31:0] CH0_Rx_Control;
  reg    [31:0] CH0_RxDesc_H;
  reg    [31:0] CH0_RxDesc_L;
  reg    [31:0] CH0_RxDesc_Tail_H;
  reg    [31:0] CH0_RxDesc_Tail_L;
  reg    [31:0] CH0_RxDesc_Ring;
  wire   [31:0] RxDesc_H_a;
  wire   [31:0] RxDesc_L_a;
  wire   [31:0] RxDesc_Tail_H_a;
  wire   [31:0] RxDesc_Tail_L_a;
  wire   [31:0] RxDesc_Ring_a;

  reg    [31:0] CH0_Tx_Control;
  reg    [31:0] CH0_TxDesc_H;
  reg    [31:0] CH0_TxDesc_L;
  reg    [31:0] CH0_TxDesc_Tail_H;
  reg    [31:0] CH0_TxDesc_Tail_L;
  reg    [31:0] CH0_TxDesc_Ring;
  wire   [31:0] TxDesc_H_a;
  wire   [31:0] TxDesc_L_a;
  wire   [31:0] TxDesc_Tail_H_a;
  wire   [31:0] TxDesc_Tail_L_a;
  wire   [31:0] TxDesc_Ring_a;

  reg    [11:0] CH0_int_En;
  reg    [11:0] CH0_int_Clr;
  reg    [11:0] CH0_int_Sta;
  wire   [11:0] int_En_a;
  reg    [11:0] int_Sta_r0;
  reg    [11:0] int_Sta_r1;
  reg    [31:0] pending_cnt;

  // interrupt
  wire          rx_tail_desc_int_en;
  wire          rx_ioc_int_en;
  wire          rx_stop_int_en;
  wire          rx_mac_int_en;

  wire          rx_timeout_int_en;

  wire          tx_tail_desc_int_en;
  wire          tx_ioc_int_en;
  wire          tx_stop_int_en;
  wire          tx_mac_int_en;

  wire          rdesc_almost_catch_flag;
  reg           rdesc_almost_catch_r1;
  reg           rdesc_almost_catch_r2;
  wire          rdesc_almost_catch_int;

  wire          tdesc_almost_catch_flag;
  reg           tdesc_almost_catch_r1;
  reg           tdesc_almost_catch_r2;
  wire          tdesc_almost_catch_int;

  reg           rx_tail_desc_int;
  reg           rx_ioc_int;
  reg           rx_stop_int;
  reg           rx_mac_int;

  reg           rx_timeout_int;

  reg           tx_tail_desc_int;
  reg           tx_ioc_int;
  reg           tx_stop_int;
  reg           tx_mac_int;
  
  wire          rx_tail_desc_int_clr;
  wire          rx_ioc_int_clr;
  wire          rx_stop_int_clr;
  wire          rx_mac_int_clr;

  wire          rx_timeout_int_clr;

  wire          tx_tail_desc_int_clr;
  wire          tx_ioc_int_clr;
  wire          tx_stop_int_clr;
  wire          tx_mac_int_clr;
  
  // register bit signals declare
  wire                 rx_start;
  wire [19:0]          rx_buff_size;
  wire                 tx_start;
  wire [19:0]          tx_buff_size;

  wire                  rx_start_r1;
  reg                   rx_start_r2;
  wire                  tx_start_r1;
  reg                   tx_start_r2;

  wire                  rx_process_flag;
  wire                  tx_process_flag;

  reg                         rx_tail_halted;
  wire                        rx_tail_new_flag;
  wire  [39:0]                rx_desc_stop_addr;
  wire                        rx_tail_cancel_flag;

  reg                         tx_tail_halted;
  wire                        tx_tail_new_flag;
  wire  [39:0]                tx_desc_stop_addr;
  wire                        tx_tail_cancel_flag;

  wire                        pending_cnt_en;

  /*+---------------------------------------------+*/
  /*|-------------------main code-----------------|*/
  /*+---------------------------------------------+*/
  assign  read_valid  = hsel_i & hready_i & htrans_i[1] & (~hwrite_i) & (haddr_i[31:16] == dma_base_addr_i[31:16]);
  assign  write_valid = hsel_i & hready_i & htrans_i[1] & hwrite_i & (haddr_i[31:16] == dma_base_addr_i[31:16]);

  always @(posedge hclk_i or negedge hresetn_i)
  begin
    if (~hresetn_i) begin
        read_enable   <= 1'b0;
        write_enable  <= 1'b0;
        word_addr     <= {12{1'b0}};
      end
    else if (hready_i) begin
        read_enable   <= read_valid;
        write_enable  <= write_valid;
        word_addr     <= nxt_word_addr;
      end
    else begin
        read_enable   <= read_enable;
        write_enable  <= write_enable;
        word_addr     <= word_addr;
      end
  end

  assign  nxt_word_addr  = haddr_i[11:0];
  assign  write_enable00 = write_enable & (word_addr[11:2] == CH0_Rx_Control_addr[11:2]);       //
  assign  write_enable04 = write_enable & (word_addr[11:2] == CH0_RxDesc_Haddr[11:2]);          //
  assign  write_enable08 = write_enable & (word_addr[11:2] == CH0_RxDesc_Laddr[11:2]);          //
  assign  write_enable0c = write_enable & (word_addr[11:2] == CH0_RxDesc_Tail_Haddr[11:2]);     //
  assign  write_enable10 = write_enable & (word_addr[11:2] == CH0_RxDesc_Tail_Laddr[11:2]);     //
  assign  write_enable14 = write_enable & (word_addr[11:2] == CH0_RxDesc_Ring_Length[11:2]);    //
  assign  write_enable18 = write_enable & (word_addr[11:2] == CH0_Tx_Control_addr[11:2]);       //
  assign  write_enable1c = write_enable & (word_addr[11:2] == CH0_TxDesc_Haddr[11:2]);          //
  assign  write_enable20 = write_enable & (word_addr[11:2] == CH0_TxDesc_Laddr[11:2]);          //
  assign  write_enable24 = write_enable & (word_addr[11:2] == CH0_TxDesc_Tail_Haddr[11:2]);     //
  assign  write_enable28 = write_enable & (word_addr[11:2] == CH0_TxDesc_Tail_Laddr[11:2]);     //
  assign  write_enable2c = write_enable & (word_addr[11:2] == CH0_TxDesc_Ring_Length[11:2]);    //
  assign  write_enable30 = write_enable & (word_addr[11:2] == CH0_Interrupt_Enable[11:2]);      //
  assign  write_enable34 = write_enable & (word_addr[11:2] == CH0_Interrupt_Status[11:2]);      // only read
  assign  write_enable38 = write_enable & (word_addr[11:2] == CH0_Interrupt_Clear[11:2]);       //
  assign  write_enable3c = write_enable & (word_addr[11:2] == CH0_Pendingout_cnt [11:2]);       //
  assign  write_enable40 = write_enable & (word_addr[11:2] == CH0_AXI_Status[11:2]);            //
  
  /*+---------------------------------------------+*/
  /*|-------------- receive registers-------------|*/
  /*+---------------------------------------------+*/
  /*************rx_control*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_Rx_Control[31:3] <= 29'h0000_0000;
    else if(write_enable00) 
      CH0_Rx_Control[31:3] <= hwdata_i[31:3];
    else
      CH0_Rx_Control[31:3] <= CH0_Rx_Control[31:3];
  end

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_Rx_Control[2:0] <= 3'b000;
    else if(write_enable00) 
      CH0_Rx_Control[2:0] <= {rx_process_flag,hwdata_i[1:0]};
    else
      CH0_Rx_Control[2:0] <= {rx_process_flag,2'b00};
  end

  ahb2axi_bit u_sync_rx_control(
    .m_axi_aclk_i         (m_axi_aclk_i),
    .m_axi_aresetn_i      (m_axi_aresetn_i),
    .signal_h             (CH0_Rx_Control[0]),
    .signal_a             (rx_start_r1)
  );

  // sync rx_start from ahbclk domain to axi clk domain
  always @(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      rx_start_r2 <= 1'b0;
    end
    else begin
      rx_start_r2 <= rx_start_r1;
    end
  end

  axi2ahb_bit u_sync_rx_process(
    .m_axi_aclk_i         (m_axi_aclk_i      ),
    .m_axi_aresetn_i      (m_axi_aresetn_i   ),
    .signal_a             (|rx_process_flag_i),
    .hclk_i               (hclk_i         ),
    .hresetn_i            (hresetn_i      ),
    .signal_h             (rx_process_flag)
  );
  assign rx_start_o = ~rx_start_r2 & rx_start_r1;
  assign pending_cnt_en = CH0_Rx_Control[1];

  /*************rx_desc*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_RxDesc_H[31:0] <= 32'h0000_0000;
    else if(write_enable04) 
      CH0_RxDesc_H[31:0] <= hwdata_i[31:0];
    else
      CH0_RxDesc_H[31:0] <= CH0_RxDesc_H[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_RxDesc_H(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable04),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (RxDesc_H_a) 
  );

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_RxDesc_L[31:0] <= 32'h0000_0000;
    else if(write_enable08) 
      CH0_RxDesc_L[31:0] <= hwdata_i[31:0];
    else
      CH0_RxDesc_L[31:0] <= CH0_RxDesc_L[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_RxDesc_L(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable08),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (RxDesc_L_a) 
  );
  assign rx_desc_addr_o = {RxDesc_H_a[7:0],RxDesc_L_a};

  /*************rx_desc_tail*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_RxDesc_Tail_H[31:0] <= 32'h0000_00FF;
    else if(write_enable0c) 
      CH0_RxDesc_Tail_H[31:0] <= hwdata_i[31:0];
    else
      CH0_RxDesc_Tail_H[31:0] <= CH0_RxDesc_Tail_H[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_RxDesc_Tail_H(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable0c),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (RxDesc_Tail_H_a) 
  );
  
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_RxDesc_Tail_L[31:0] <= 32'hFFFF_FFFF;
    else if(write_enable10) 
      CH0_RxDesc_Tail_L[31:0] <= hwdata_i[31:0];
    else
      CH0_RxDesc_Tail_L[31:0] <= CH0_RxDesc_Tail_L[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_RxDesc_Tail_L(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable10),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (RxDesc_Tail_L_a) 
  );

  // Tail address should always be in front of current,less than 32 desc will interrupt
  assign rdesc_almost_catch_flag = ({RxDesc_Tail_H_a[7:0],RxDesc_Tail_L_a} <= (40'h00_0000_01F0 + rdesc_addr_current_i));

  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i) begin
    if (~m_axi_aresetn_i) begin
      rdesc_almost_catch_r1 <= 1'b0;
      rdesc_almost_catch_r2 <= 1'b0;
    end
    else begin
      rdesc_almost_catch_r1 <= |rx_process_flag_i[1:0] ? rdesc_almost_catch_flag : 1'b0;
      rdesc_almost_catch_r2 <= |rx_process_flag_i[1:0] ? rdesc_almost_catch_r1 : 1'b0;
    end
  end

  assign rdesc_almost_catch_int = rdesc_almost_catch_r1;//~rdesc_almost_catch_r2 & rdesc_almost_catch_r1;

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_RxDesc_Ring[31:0] <= 32'hFFFF_FFFF;
    else if(write_enable14) 
      CH0_RxDesc_Ring[31:0] <= hwdata_i[31:0];
    else
      CH0_RxDesc_Ring[31:0] <= CH0_RxDesc_Ring[31:0];
  end
  
  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_RxDesc_Ring(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable14),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (RxDesc_Ring_a) 
  );

  assign rxdesc_ring_length_o = RxDesc_Ring_a;

  /*+---------------------------------------------+*/
  /*|------------- transmit registers-------------|*/
  /*+---------------------------------------------+*/
  /*************tx_control*************/
    always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_Tx_Control[31:3] <= 29'h0000_0000;
    else if(write_enable18) 
      CH0_Tx_Control[31:3] <= hwdata_i[31:3];
    else
      CH0_Tx_Control[31:3] <= CH0_Tx_Control[31:3];
  end

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_Tx_Control[2:0] <= 3'b000;
    else if(write_enable18) 
      CH0_Tx_Control[2:0] <= {tx_process_flag,1'b0,hwdata_i[0]};
    else
      CH0_Tx_Control[2:0] <= {tx_process_flag,2'b00};
  end

  ahb2axi_bit u_sync_tx_control(
    .m_axi_aclk_i         (m_axi_aclk_i),
    .m_axi_aresetn_i      (m_axi_aresetn_i),
    .signal_h             (CH0_Tx_Control[0]),
    .signal_a             (tx_start_r1)
  );
  
  // sync tx_start from ahbclk domain to axi clk domain
  always @(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      tx_start_r2 <= 1'b0;
    end
    else begin
      tx_start_r2 <= tx_start_r1;
    end
  end

  axi2ahb_bit u_sync_tx_process(
    .m_axi_aclk_i         (m_axi_aclk_i      ),
    .m_axi_aresetn_i      (m_axi_aresetn_i   ),
    .signal_a             (|tx_process_flag_i),
    .hclk_i               (hclk_i         ),
    .hresetn_i            (hresetn_i      ),
    .signal_h             (tx_process_flag)
  );

  assign tx_start_o = ~tx_start_r2 & tx_start_r1;
  
  /*************tx_desc*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_TxDesc_H[31:0] <= 32'h0000_0000;
    else if(write_enable1c) 
      CH0_TxDesc_H[31:0] <= hwdata_i[31:0];
    else
      CH0_TxDesc_H[31:0] <= CH0_TxDesc_H[31:0];
  end
  
  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_TxDesc_H(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable1c),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (TxDesc_H_a) 
  );

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_TxDesc_L[31:0] <= 32'h0000_0000;
    else if(write_enable20) 
      CH0_TxDesc_L[31:0] <= hwdata_i[31:0];
    else
      CH0_TxDesc_L[31:0] <= CH0_TxDesc_L[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_TxDesc_L(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable20),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (TxDesc_L_a) 
  );

  assign tx_desc_addr_o = {TxDesc_H_a[7:0],TxDesc_L_a};
  
  /*************tx_desc_tail*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_TxDesc_Tail_H[31:0] <= 32'h0000_00FF;
    else if(write_enable24) 
      CH0_TxDesc_Tail_H[31:0] <= hwdata_i[31:0];
    else
      CH0_TxDesc_Tail_H[31:0] <= CH0_TxDesc_Tail_H[31:0];
  end
  
  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_TxDesc_Tail_H(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable24),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (TxDesc_Tail_H_a) 
  );

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_TxDesc_Tail_L[31:0] <= 32'hFFFF_FFFF;
    else if(write_enable28) 
      CH0_TxDesc_Tail_L[31:0] <= hwdata_i[31:0];
    else
      CH0_TxDesc_Tail_L[31:0] <= CH0_TxDesc_Tail_L[31:0];
  end

  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_TxDesc_Tail_L(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable28),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (TxDesc_Tail_L_a) 
  );

  // Tail address should always be in front of current,less than 32 desc will interrupt, minus is abnormal
  assign tdesc_almost_catch_flag = ({TxDesc_Tail_H_a[7:0],TxDesc_Tail_L_a} <= (40'h00_0000_01F0 + tdesc_addr_current_i));

  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i) begin
    if (~m_axi_aresetn_i) begin
      tdesc_almost_catch_r1 <= 1'b0;
      tdesc_almost_catch_r2 <= 1'b0;
    end
    else begin
      tdesc_almost_catch_r1 <= |tx_process_flag_i[1:0] ? tdesc_almost_catch_flag : 1'b0;
      tdesc_almost_catch_r2 <= |tx_process_flag_i[1:0] ? tdesc_almost_catch_r1 : 1'b0;
    end
  end

  assign tdesc_almost_catch_int = tdesc_almost_catch_r1;//~tdesc_almost_catch_r2 & tdesc_almost_catch_r1;

  /*************tx_desc_tail*************/
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_TxDesc_Ring[31:0] <= 32'hFFFF_FFFF;
    else if(write_enable2c) 
      CH0_TxDesc_Ring[31:0] <= hwdata_i[31:0];
    else
      CH0_TxDesc_Ring[31:0] <= CH0_TxDesc_Ring[31:0];
  end
  
  ahb2axi_multi_bits #(
    .width(32)
  )u_sync_TxDesc_Ring(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable2c),
    .signal_h           (hwdata_i[31:0]),
    .signal_a           (TxDesc_Ring_a) 
  );

  assign txdesc_ring_length_o = TxDesc_Ring_a;

  /*+---------------------------------------------+*/
  /*|---------------timeout registers-------------|*/
  /*+---------------------------------------------+*/
  reg   pending_load_en;
  wire  pending_cnt_stop;
  reg   rxfifo_timeout_r0;
  reg   rxfifo_timeout_r1;
  reg   rxfifo_timeout_r2;

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      pending_cnt <= {32{1'b1}};
    else if(write_enable3c) 
      pending_cnt <= hwdata_i[31:0];
    else
      pending_cnt <= pending_cnt;
  end

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      pending_load_en <= 1'b0;
    else
      pending_load_en <= write_enable3c;
  end
  
  reg [4:0]  rx_fifo_empty_shift;
  reg        rx_fifo_empty_flag_r0;
  reg        rx_fifo_empty_flag_r1;

  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i) begin
    if (~m_axi_aresetn_i) begin
      rx_fifo_empty_shift <= 5'b11111;
    end
    else begin
      rx_fifo_empty_shift <= {rx_fifo_empty_shift[3:0],rx_fifo_empty_i};
    end
  end

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i) begin
      rx_fifo_empty_flag_r0 <= 1'b1;
      rx_fifo_empty_flag_r1 <= 1'b1;
    end
    else begin
      rx_fifo_empty_flag_r0 <= &rx_fifo_empty_shift;
      rx_fifo_empty_flag_r1 <= rx_fifo_empty_flag_r0;
    end
  end

  np_dma_pending_cnt u_np_dma_pending_cnt(
    .clk_i                        (hclk_i), //125MHz
    .resetn_i                     (hresetn_i),
    .load_en_i                    (pending_load_en),
    .load_pending_time_i          (pending_cnt),
    .pending_en_i                 (pending_cnt_en),
    .cnt_en_i                     (rx_fifo_empty_flag_r1),
    .cnt_stop_o                   (pending_cnt_stop)
  );
  
  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i) begin
    if (~m_axi_aresetn_i) begin
      rxfifo_timeout_r0 <= 1'b1;
      rxfifo_timeout_r1 <= 1'b1;
      rxfifo_timeout_r2 <= 1'b1;
    end
    else if(|rx_process_flag_i[1:0])begin
      rxfifo_timeout_r0 <= pending_cnt_stop;
      rxfifo_timeout_r1 <= rxfifo_timeout_r0;
      rxfifo_timeout_r2 <= rxfifo_timeout_r1;
    end
    else begin
      rxfifo_timeout_r0 <= 1'b1;
      rxfifo_timeout_r1 <= 1'b1;
      rxfifo_timeout_r2 <= 1'b1;
    end
  end

  assign rxfifo_timeout_valid_o = ~rxfifo_timeout_r2 & rxfifo_timeout_r1;

  /*+---------------------------------------------+*/
  /*|------------- interrupt registers------------|*/
  /*+---------------------------------------------+*/
  
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_int_En[11:0] <= 12'h000;
    else if(write_enable30) 
      CH0_int_En[11:0] <= hwdata_i[11:0];
    else
      CH0_int_En[11:0] <= CH0_int_En[11:0];
  end

  ahb2axi_multi_bits #(
    .width(12)
  )u_sync_int_En(
    .m_axi_aclk_i       (m_axi_aclk_i   ),
    .m_axi_aresetn_i    (m_axi_aresetn_i),
    .valid_h            (write_enable30),
    .signal_h           (hwdata_i[11:0]),
    .signal_a           (int_En_a) 
  );

  assign rx_tail_desc_int_en = int_En_a[11];
  assign rx_ioc_int_en       = int_En_a[10];
  assign rx_stop_int_en      = int_En_a[9];
  assign rx_mac_int_en       = int_En_a[8];

  assign rx_timeout_int_en   = int_En_a[4];

  assign tx_tail_desc_int_en = int_En_a[3];
  assign tx_ioc_int_en       = int_En_a[2];
  assign tx_stop_int_en      = int_En_a[1];
  assign tx_mac_int_en       = int_En_a[0];

//------------------------------------------------------------------------------
// Interrupt clear
//------------------------------------------------------------------------------
//  The interrupt clear is extended to ensure that it remains valid until the 
//  interrupt is actually cleared in the m_axi_aclk_i domain. A toggle based 
//  handshake is used in case hclk_i is removed before the interrupt clear 
//  operation is complete.
  reg         ahb_clk_catched;
  reg         axi_clk_catched;

  reg         int_clr_tog_h;
  reg         int_clr_tog_a;

  wire        int_clr_pulse;
  wire        int_clr_wrote;

  reg  [11:0]  int_clr_axi;

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i)
      CH0_int_Clr[11:0] <= 12'h000;
    else if(write_enable38) 
      CH0_int_Clr[11:0] <= hwdata_i[11:0];
    else if(int_clr_wrote)
      CH0_int_Clr[11:0] <= 12'h000;
    else
      CH0_int_Clr[11:0] <= CH0_int_Clr[11:0];
  end

  // IntClr high toggles int_clr_tog_h on next hclk_i
  always @(negedge hresetn_i or posedge hclk_i)
  begin : p_int_clr_tog_h_seq
    if (~hresetn_i)
      int_clr_tog_h <= 1'b0;
    else if (write_enable38)
      int_clr_tog_h <= (~int_clr_tog_h);
    else
      int_clr_tog_h <= int_clr_tog_h;
  end

  // Register int_clr_tog_h into m_axi_aclk_i domain
  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i)
  begin : p_int_clr_tog_w_seq
    if (~m_axi_aresetn_i)
      int_clr_tog_a <= 1'b0;
    else
      int_clr_tog_a <= int_clr_tog_h;
  end

  // int_clr_pulse is high on hclk_i edge, low on m_axi_aclk_i edge
  assign int_clr_pulse = int_clr_tog_h ^ int_clr_tog_a;

  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i)  begin
    if (~m_axi_aresetn_i)
      axi_clk_catched <= 1'b0;
    else if (int_clr_pulse)
      axi_clk_catched <= (~axi_clk_catched);
    else
      axi_clk_catched <= axi_clk_catched;
  end
  
  always @(posedge hclk_i or negedge hresetn_i) begin
    if (~hresetn_i)
      ahb_clk_catched <= 1'b0;
    else
      ahb_clk_catched <= axi_clk_catched;
  end
  // int_clr_wrote is high on m_axi_aclk_i edge, low on hclk_i edge
  assign int_clr_wrote = axi_clk_catched ^ ahb_clk_catched;

  /*+---------------------------------------------+*/
  /*|---------------interrupt control-------------|*/
  /*+---------------------------------------------+*/
  
  always @(negedge m_axi_aresetn_i or posedge m_axi_aclk_i)
  begin
    if (~m_axi_aresetn_i)
      int_clr_axi <= 12'h000;
    else if (int_clr_pulse)
      int_clr_axi <= CH0_int_Clr;
    else
      int_clr_axi <= 12'h000;
  end

  assign rx_tail_desc_int_clr  = int_clr_axi[11];
  assign rx_ioc_int_clr        = int_clr_axi[10];
  assign rx_stop_int_clr       = int_clr_axi[9];
  assign rx_mac_int_clr        = int_clr_axi[8];

  assign rx_timeout_int_clr    = int_clr_axi[4];

  assign tx_tail_desc_int_clr  = int_clr_axi[3];
  assign tx_ioc_int_clr        = int_clr_axi[2];
  assign tx_stop_int_clr       = int_clr_axi[1];
  assign tx_mac_int_clr        = int_clr_axi[0];

  assign rx_buf_unavlib_int_clr_o = rx_tail_desc_int_clr;
  assign tx_buf_unavlib_int_clr_o = tx_tail_desc_int_clr;

  assign clr_rx_complete_o = rx_stop_int_clr;
  assign clr_tx_complete_o = tx_stop_int_clr;

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_tail_desc_int <= 1'b0;
    else if(rx_tail_desc_int_clr) 
      rx_tail_desc_int <= 1'b0;
    else if(rx_tail_desc_int_en & (~rx_tail_cancel_flag) & rdesc_almost_catch_int) 
      rx_tail_desc_int <= 1'b1;
    else
      rx_tail_desc_int <= rx_tail_desc_int;
  end
  
  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_ioc_int <= 1'b0;
    else if(rx_ioc_int_clr) 
      rx_ioc_int <= 1'b0;
    else if(rx_ioc_int_en & rx_ioc_int_i) 
      rx_ioc_int <= 1'b1;
    else
      rx_ioc_int <= rx_ioc_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_stop_int <= 1'b0;
    else if(rx_stop_int_clr) 
      rx_stop_int <= 1'b0;
    else if(rx_stop_int_en & rx_complete_int_i) 
      rx_stop_int <= 1'b1;
    else
      rx_stop_int <= rx_stop_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_mac_int <= 1'b0;
    else if(rx_mac_int_clr) 
      rx_mac_int <= 1'b0;
    //else if(rx_mac_int_en & rx_mac_int_i) old
    else if(rx_mac_int_i) //0620
      rx_mac_int <= 1'b1;
    else
      rx_mac_int <= rx_mac_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_timeout_int <= 1'b0;
    else if(rx_timeout_int_clr) 
      rx_timeout_int <= 1'b0;
    else if(rx_timeout_int_en & rxfifo_timeout_valid_o) 
      rx_timeout_int <= 1'b1;
    else
      rx_timeout_int <= rx_timeout_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      tx_tail_desc_int <= 1'b0;
    else if(tx_tail_desc_int_clr) 
      tx_tail_desc_int <= 1'b0;
    else if(tx_tail_desc_int_en & (~tx_tail_cancel_flag) & tdesc_almost_catch_int) 
      tx_tail_desc_int <= 1'b1;
    else
      tx_tail_desc_int <= tx_tail_desc_int;
  end
  
  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      tx_ioc_int <= 1'b0;
    else if(tx_ioc_int_clr) 
      tx_ioc_int <= 1'b0;
    else if(tx_ioc_int_en & tx_ioc_int_i) 
      tx_ioc_int <= 1'b1;
    else
      tx_ioc_int <= tx_ioc_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      tx_stop_int <= 1'b0;
    else if(tx_stop_int_clr) 
      tx_stop_int <= 1'b0;
    else if(tx_stop_int_en & tx_complete_int_i) 
      tx_stop_int <= 1'b1;
    else
      tx_stop_int <= tx_stop_int;
  end

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      tx_mac_int <= 1'b0;
    else if(tx_mac_int_clr) 
      tx_mac_int <= 1'b0;
    else if(tx_mac_int_en & tx_mac_int_i) 
      tx_mac_int <= 1'b1;
    else
      tx_mac_int <= tx_mac_int;
  end
//1000-32=968,<968 int,980,1000-980<32,int

  assign rx_desc_stop_addr = rx_desc_addr_o + {4'h0,RxDesc_Ring_a,4'h0};
  assign rx_tail_new_flag = ({RxDesc_Tail_H_a[7:0],RxDesc_Tail_L_a} > (40'h00_0000_01F0 + rdesc_addr_current_i)) ;
  assign rx_tail_cancel_flag = ({RxDesc_Tail_H_a[7:0],RxDesc_Tail_L_a} >= rx_desc_stop_addr);

  assign tx_desc_stop_addr = tx_desc_addr_o + {4'h0,TxDesc_Ring_a,4'h0};
  assign tx_tail_new_flag = ({TxDesc_Tail_H_a[7:0],TxDesc_Tail_L_a} > (40'h00_0000_01F0 + tdesc_addr_current_i));
  assign tx_tail_cancel_flag = ({TxDesc_Tail_H_a[7:0],TxDesc_Tail_L_a} >= tx_desc_stop_addr);

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      rx_tail_halted <= 1'b0;
    else if(rx_tail_new_flag | rx_tail_cancel_flag) 
      rx_tail_halted <= 1'b0;
    //else if(({CH0_RxDesc_Tail_H[7:0],CH0_RxDesc_Tail_L} - rdesc_addr_current_i) == 40'h00_0000_0000)
    else if(rdesc_almost_catch_int) 
      rx_tail_halted <= 1'b1;
    else
      rx_tail_halted <= rx_tail_halted;
  end
  assign rx_tail_halted_o = rx_tail_halted;

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i)
      tx_tail_halted <= 1'b0;
    else if(tx_tail_new_flag | tx_tail_cancel_flag) 
      tx_tail_halted <= 1'b0;
    //else if(({CH0_TxDesc_Tail_H[7:0],CH0_TxDesc_Tail_L} - tdesc_addr_current_i) == 40'h00_0000_0000)
    else if(tdesc_almost_catch_int) 
      tx_tail_halted <= 1'b1;
    else
      tx_tail_halted <= tx_tail_halted;
  end
  assign tx_tail_halted_o = tx_tail_halted;

  always @(*) begin
    CH0_int_Sta = {rx_tail_desc_int,rx_ioc_int,rx_stop_int,rx_mac_int & rx_mac_int_en,3'b000,rx_timeout_int,tx_tail_desc_int,tx_ioc_int,tx_stop_int,tx_mac_int};
  end

  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i) begin
      int_Sta_r0[11:0] <= 12'h000;
      int_Sta_r1[11:0] <= 12'h000;
    end
    else begin
      int_Sta_r0[11:0] <= CH0_int_Sta[11:0];
      int_Sta_r1[11:0] <= int_Sta_r0[11:0];
    end
  end

  /*+---------------------------------------------+*/
  /*|-----------------output to AHB---------------|*/
  /*+---------------------------------------------+*/

  always @(*) begin
      if (haddr_i[11:9] == 3'h0) begin
        case (haddr_i[8:2])
          CH0_Rx_Control_addr[8:2]    :   read_mux_word = CH0_Rx_Control;
          CH0_RxDesc_Haddr[8:2]       :   read_mux_word = CH0_RxDesc_H;
          CH0_RxDesc_Laddr[8:2]	      :   read_mux_word = CH0_RxDesc_L;
          CH0_RxDesc_Tail_Haddr[8:2]	:   read_mux_word = CH0_RxDesc_Tail_H;
          CH0_RxDesc_Tail_Laddr[8:2]	:   read_mux_word = CH0_RxDesc_Tail_L;
          CH0_RxDesc_Ring_Length[8:2] :   read_mux_word = CH0_RxDesc_Ring;
          CH0_Tx_Control_addr[8:2]	  :   read_mux_word = CH0_Tx_Control;
          CH0_TxDesc_Haddr[8:2]       :   read_mux_word = CH0_TxDesc_H;
          CH0_TxDesc_Laddr[8:2]	      :   read_mux_word = CH0_TxDesc_L;
          CH0_TxDesc_Tail_Haddr[8:2]	:   read_mux_word = CH0_TxDesc_Tail_H;
          CH0_TxDesc_Tail_Laddr[8:2]	:   read_mux_word = CH0_TxDesc_Tail_L;
          CH0_TxDesc_Ring_Length[8:2] :   read_mux_word = CH0_TxDesc_Ring;
          CH0_Interrupt_Enable[8:2]	  :   read_mux_word = {{20{1'b0}},CH0_int_En};
          CH0_Interrupt_Status[8:2]	  :   read_mux_word = {{20{1'b0}},int_Sta_r1};
          CH0_Interrupt_Clear[8:2]    :   read_mux_word = {{20{1'b0}},CH0_int_Clr};
          CH0_Pendingout_cnt[8:2]	    :   read_mux_word = pending_cnt;
          default : read_mux_word = {32{1'b0}};
        endcase
    end
      else
        read_mux_word = {32{1'b0}};
  end
  
  always@(posedge hclk_i or negedge hresetn_i)
  if(~hresetn_i)
    read_word <= 32'b0;
  else if(read_valid)
    read_word <= read_mux_word;
  else
    read_word <= read_word;

  // Connect to output

  assign    hreadyout_o = 1'b1;
  assign    hrdata_o     = read_enable ? read_word : {32{1'b0}};
  assign    hresp_o     = 1'b0;
  assign    np_dma_irq_o = rx_tail_desc_int | rx_ioc_int | rx_stop_int | rx_mac_int & rx_mac_int_en | rx_timeout_int | tx_tail_desc_int | tx_ioc_int | tx_stop_int | tx_mac_int;

endmodule

module ahb2axi_bit (
    input  wire            m_axi_aclk_i,
    input  wire            m_axi_aresetn_i,
    input  wire            signal_h,
    output wire            signal_a
  );
  reg         tmp1;
  reg         tmp2;

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      tmp1 <= 1'b0;
      tmp2 <= 1'b0;
    end
    else begin
      tmp1 <= signal_h;
      tmp2 <= tmp1;
    end
  end
  assign signal_a = tmp2;
  
endmodule

module ahb2axi_multi_bits #(
    parameter width = 32
  )(
    input  wire                       m_axi_aclk_i,
    input  wire                       m_axi_aresetn_i,
    input  wire                       valid_h,
    input  wire [width - 1 : 0]       signal_h,
    output wire [width - 1 : 0]       signal_a
  );
  reg [width - 1 : 0]       tmp1;
  wire                      tmp_vld;
  reg                       tmp_vld_r;
  wire                      tmp_vld_p;

  ahb2axi_bit u_ahb2axi_bit(
    .m_axi_aclk_i            (m_axi_aclk_i      ),
    .m_axi_aresetn_i         (m_axi_aresetn_i   ),
    .signal_h                (valid_h           ),
    .signal_a                (tmp_vld           )
  );

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      tmp_vld_r <= 1'b0;
    end
    else begin
      tmp_vld_r <= tmp_vld;
    end
  end
  assign tmp_vld_p = ~tmp_vld_r & tmp_vld;

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      tmp1 <= {width{1'b0}};
    end
    else if(tmp_vld_p)begin
      tmp1 <= signal_h;
    end
    else begin
      tmp1 <= tmp1;
    end
  end
  assign signal_a = tmp1;

endmodule

module axi2ahb_bit (
    input   wire            m_axi_aclk_i,
    input   wire            m_axi_aresetn_i,
    input   wire            signal_a,
    input   wire            hclk_i,
    input   wire            hresetn_i,
    output  wire            signal_h
  );
  reg [3:0]   tmp1;
  reg         tmp2;
  reg         tmp3;

  always@(posedge m_axi_aclk_i or negedge m_axi_aresetn_i) begin
    if(~m_axi_aresetn_i) begin
      tmp1 <= 4'b0000;
    end
    else begin
      tmp1 <= {tmp1[2:0],signal_a};
    end
  end
  
  always@(posedge hclk_i or negedge hresetn_i) begin
    if(~hresetn_i) begin
      tmp2 <= 1'b0;
      tmp3 <= 1'b0;
    end
    else begin
      tmp2 <= |tmp1;
      tmp3 <= tmp2;
    end
  end

  assign signal_h = tmp3;
  
endmodule